home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / c / comnumb.exe / COMPOUN.CPP < prev    next >
C/C++ Source or Header  |  1992-05-01  |  10KB  |  360 lines

  1. /////////////////////////////////////////////////////////////////
  2. // compoun.cpp: Compound number class implementation.
  3. // Copyright (c) 1992 Azarona Software. All rights reserved.
  4. // NOTE: Many of the routines here could be made inline for
  5. // significant performance gains.
  6. /////////////////////////////////////////////////////////////////
  7. #include <ctype.h>
  8. #include "compoun.h"
  9.  
  10. Compound::Compound()
  11. // Default constructor sets number to "undefined."
  12. // Default Rational() constructor called implicitly
  13. : w(0)
  14. {
  15.   // Nothing else to do
  16. }
  17.  
  18. Compound::Compound(long n)
  19. // Converts whole number n into compound number <n 0/1>.
  20. : w(n), f(0)
  21. {
  22.   // Nothing else to do
  23. }
  24.  
  25. Compound::Compound(long n, Rational &r)
  26. // Constructors compound number <n r>.
  27. {
  28.   Set(n, r);
  29. }
  30.  
  31. Compound::Compound(Compound const &c)
  32. // Copy constructor
  33. : w(c.w), f(c.f)
  34. {
  35.   // Nothing else to do
  36. }
  37.  
  38. Compound &Compound::operator=(Compound const &c)
  39. // Overloaded assignment.
  40. {
  41.   w = c.w; f = c.f; return *this;
  42. }
  43.  
  44. void Compound::Simplify()
  45. // Simplifies the compound number.
  46. // ASSUMES fractional part is already simplified.
  47. {
  48.   if (f.IsUndefined()) {
  49.      w = 0; // So we have <0 0/0> for undefined
  50.   }
  51.   else {
  52.     // Split f into whole number plus fraction.
  53.     long w1 = f.RemoveWholePart();
  54.     w += w1; // Add whole number from split to w.
  55.     // If whole number and fraction have different signs,
  56.     // we must force them to have same signs and simplify
  57.     // further.
  58.     if (w > 0 && f.IsNegative()) {
  59.        // We're using the formula: w + f = (w-1) + (f+1).
  60.        w--;
  61.        f++;
  62.     }
  63.     else if (w < 0 && f.IsPositive()) {
  64.        // We're using the formula: w + f = (w+1) + (f-1).
  65.        w++;
  66.        f--;
  67.     }
  68.   }
  69. }
  70.  
  71. void Compound::Set(long n, Rational &r)
  72. {
  73.   w = n; f = r;
  74.   Simplify();
  75. }
  76.  
  77. void Compound::Negate()
  78. // Take the negative of this compound number.
  79. {
  80.   w = -w; 
  81.   f.Negate();
  82. }
  83.  
  84. void Compound::Invert()
  85. // Divide 1 by this number, store result in this number.
  86. // Uses formula: 1/c = 1/(w + n/d) = d/(w*d + n).
  87. // Result is then simplified.
  88. // If number is zero, we'll get a result of <0 0/0>.
  89. // WARNING: Overflow is not checked!
  90. {
  91.   Rational r(f.Denominator(), w*f.Denominator() + f.Numerator());
  92.   operator=(Compound(0, r));
  93. }
  94.  
  95. int Compound::IsUndefined() const
  96. {
  97.   return f.IsUndefined();
  98. }
  99.  
  100. int Compound::IsNegative() const
  101. {
  102.   return w < 0 || (w == 0 && f.IsNegative());
  103. }
  104.  
  105. int Compound::IsPositive() const
  106. {
  107.   return w > 0 || (w == 0 && f.IsPositive());
  108. }
  109.  
  110. int Compound::IsZero() const
  111. {
  112.   return w == 0 && f.IsZero();
  113. }
  114.  
  115.  
  116. istream &operator>>(istream &s, Compound &cn)
  117. // Reads in a compound number. Legal syntaxes are:
  118. // w, <n/d>, and <w n/d>. Appropriate whitespace
  119. // is allowed. If the stream s is not in a good state upon 
  120. // return, then cn will contain <0 0/0>.
  121. {
  122.   char c, syntax_error = 0;
  123.   long n, d;
  124.  
  125.   if (s >> c) { // Read in first non-whitespace character
  126.      if (isdigit(c) || (c == '-') || (c == '+')) {
  127.         // We have a whole number. Put back character
  128.         // and then read in the whole number.
  129.         s.putback(c);
  130.         if (s >> cn.w) {
  131.            cn.f = 0; // We have zero fractional part
  132.         }
  133.      }
  134.      else if (c == '<') {
  135.         // Reading in <w n/d> or <n/d> syntax
  136.         if (s >> n) {
  137.            // Might have w or n, find out by looking for '/'
  138.            if (s >> c) {
  139.               if (c == '/') {
  140.                  // Guess we have n. That means w = 0. Grab d.
  141.                  cn.w = 0;
  142.                  s >> d; 
  143.               }
  144.               else { // We have w. Putback c, look for n/d.
  145.                 cn.w = n;
  146.                 s.putback(c);
  147.                 if (s >> n) { // We've got numerator
  148.                    if (s >> c) { // Look for '/'
  149.                       if (c == '/') 
  150.                          s >> d; 
  151.                          else syntax_error = 1;
  152.                    }
  153.                 }
  154.               }
  155.               if (s) { // Good so far, so look for '>'
  156.                  if (s >> c) {
  157.                     if (c == '>') { // Good read
  158.                        cn.f.Set(n, d);
  159.                        cn.Simplify();
  160.                     }
  161.                     else syntax_error = 1;
  162.                  }
  163.               }
  164.            }
  165.         }
  166.      }
  167.      else syntax_error = 1;
  168.   }
  169.   if (syntax_error) s.clear(ios::failbit);
  170.   if (!s) { // Stream failure, so result is undefined
  171.      cn.w = 0; cn.f.Set(0, 0);
  172.   }
  173.   return s;
  174. }
  175.  
  176. ostream &operator<<(ostream &s, const Compound &c)
  177. // Output compound number c to stream. Outputs "und" 
  178. // for <0 0/0>. If fraction part is zero, it is not shown.
  179. {
  180.   if (c.IsUndefined()) return s << "und";
  181.   if (c.f.IsZero()) return s << c.w;
  182.   s << '<';
  183.   if (c.w != 0) s << c.w << ' ';
  184.   s << c.f.Numerator() << '/' << c.f.Denominator();
  185.   return s << '>';
  186. }
  187.  
  188. Compound Compound::operator-() const
  189. // Unary - operator. Computes negative of this number
  190. // and returns a copy.
  191. {
  192.   return Compound(-w, -f);
  193. }
  194.  
  195. Compound Compound::operator+() const
  196. // Unary + operator. Returns a copy of this number.
  197. // (Thus, semantics are consistent with unary -).
  198. {
  199.   return Compound(*this);
  200. }
  201.  
  202. Compound &Compound::operator+=(const Compound &r)
  203. // Adds r to this number. Stores the result in this number.
  204. {
  205.   return operator=(*this + r);
  206. }
  207.  
  208. Compound &Compound::operator-=(const Compound &r)
  209. // Subtracts r from this number. Stores the result in 
  210. // this number.
  211. {
  212.   return operator=(*this - r);
  213. }
  214.  
  215. Compound &Compound::operator*=(const Compound &r)
  216. // Mutliplies this number by r. Stores the result in 
  217. // this number.
  218. {
  219.   return operator=(*this * r);
  220. }
  221.  
  222. Compound &Compound::operator/=(const Compound &r)
  223. // Divides this number by r. Stores the result in 
  224. // this number.
  225. {
  226.   return operator=(*this / r);
  227. }
  228.  
  229. Compound &Compound::operator++()
  230. // Prefix increment operator. Safe to use
  231. // in expressions like ++(++r).
  232. {
  233.   return *this += 1;
  234. }
  235.  
  236. Compound &Compound::operator--()
  237. // Prefix decrement operator. Safe to use
  238. // in expressions like --(--r).
  239. {
  240.   return *this -= 1;
  241. }
  242.  
  243. Compound Compound::operator++(int)
  244. // Postfix increment. Note that we don't return a reference
  245. // as we do with prefix increment. Instead a copy of the result
  246. // is returned. Thus, expressions like (c++)++ will not return
  247. // the correct result.
  248. // WARNING: Result not checked for possible overflow.
  249. {
  250.   Compound old(*this); 
  251.   *this += 1;
  252.   return old;
  253. }
  254.  
  255. Compound Compound::operator--(int)
  256. // Postfix decrement. Note that we don't return a reference
  257. // as we do with prefix decrement. Instead a copy of the result
  258. // is returned. Thus, expressions like (c--)-- will not return
  259. // the correct result.
  260. // WARNING: Result not checked for possible overflow.
  261. {
  262.   Compound old(*this);
  263.   *this -= 1;
  264.   return old;
  265. }
  266.  
  267. Compound operator+(const Compound &a, const Compound &b)
  268. // Adds a and b, returning the result. Uses the formula:
  269. // a + b = (aw + af) + (bw + bf) = (aw + bw) + (af + bf)
  270. // and simplifies the result.
  271. {
  272.   return Compound(a.w + b.w, a.f + b.f);
  273. }
  274.  
  275. Compound operator-(const Compound &a, const Compound &b)
  276. // Subtracts b from a and returns result. Uses the
  277. // operator+() function to do the dirty work.
  278. // WARNING: Result not checked for possible overflow.
  279. {
  280.   Compound nb(b);
  281.   nb.Negate();
  282.   return a + nb;  // Ie: a + (-b)
  283. }
  284.  
  285. Compound operator*(const Compound &a, const Compound &b)
  286. // Multiplies a and b together, returning the result.
  287. // Uses the formula: a * b = (aw + af) * (bw + bf) 
  288. //                         = aw*bw + af*bf + aw*bf + af*bw
  289. // In the calculations, we keep extracting the whole part
  290. // of the fraction terms, to keep down chances of overflow.
  291. {
  292.   long rw = a.w * b.w;
  293.   Rational rf(a.f * b.f);
  294.   rw += rf.RemoveWholePart();
  295.   rf += a.w * b.f;
  296.   rw += rf.RemoveWholePart();
  297.   rf += a.f * b.w;
  298.   rw += rf.RemoveWholePart();
  299.   return Compound(rw, rf);
  300. }
  301.  
  302. Compound operator/(const Compound &a, const Compound &b)
  303. // Divides a by b, returning the result. Note that
  304. // operator*() does all the dirty